package com.sdses.weather.config.mapping;

import cn.hutool.core.collection.CollUtil;
import com.sdses.weather.common.MappingResult;
import com.sdses.weather.config.WeatherProviderSchemaConfig;
import com.sdses.weather.config.exception.BusinessException;
import com.sdses.weather.utils.SortHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.PropertyUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Slf4j
@Component
public class FieldInstructionCompiler implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    // 缓存：Class + 字段配置 → 指令集
    private final ConcurrentHashMap<String, FieldInstruction<?>[]> instructionCache = new ConcurrentHashMap<>();

    @Autowired
    private WeatherProviderSchemaConfig weatherProviderSchemaConfig;

    // ==================== 主入口：带全局上下文的列表映射 ====================
    public <T, C> MappingResult<List<T>, C> mapDataListWithGlobal(
            String json,
            String provider,
            String dataType,
            Class<T> itemClass,
            Class<C> contextClass) {

        try {
            WeatherProviderSchemaConfig.ProviderConfig providerConfig = weatherProviderSchemaConfig.getProvider(provider);

            // 2. 获取数据类型配置
            WeatherProviderSchemaConfig.DataTypeConfig typeConfig = WeatherProviderSchemaConfig.getDataTypeConfig(providerConfig, dataType);

            // 3. 解析数据列表
            List<T> itemList = mapDataList(json, providerConfig, typeConfig, itemClass, provider);

            // 4. 解析全局上下文（如 baseTimestamp, conditionCode）
            C context = (CollUtil.isNotEmpty(typeConfig.getContextFields()) && contextClass != null)
                    ? parseContext(json, providerConfig.getBasePath(), typeConfig.getContextFields(), contextClass, provider)
                    : WeatherMappingUtil.instantiate(contextClass); // 上下文无配置，返回空实例

            // 5. 返回结果
            MappingResult<List<T>, C> result = new MappingResult<>();
            result.setData(itemList);
            result.setContext(context);
            return result;

        } catch (Exception e) {
            log.error("Failed to map data with global context [provider={}, dataType={}]", provider, dataType, e);
            throw new BusinessException("数据映射失败: " + e.getMessage());
        }
    }

    public <T> FieldInstruction<T>[] compile(
            Class<T> targetType,
            List<WeatherProviderSchemaConfig.FieldMapping> fieldMappings,
            String provider) {

        String cacheKey = targetType.getName() + "_" + fieldMappings.hashCode();

        return (FieldInstruction<T>[]) instructionCache.computeIfAbsent(cacheKey, k -> {
            List<FieldInstruction<T>> instructions = new ArrayList<>();

            for (WeatherProviderSchemaConfig.FieldMapping field : fieldMappings) {
                FieldInstruction<T> instruction = compileInstruction(targetType, field, provider);
                if (instruction != null) {
                    instructions.add(instruction);
                }
            }

            return instructions.toArray(new FieldInstruction[0]);
        });
    }

    private <T> FieldInstruction<T> compileInstruction(
            Class<T> targetType,
            WeatherProviderSchemaConfig.FieldMapping field,
            String provider) {

        String javaFieldName = WeatherMappingUtil.getTargetFieldName(field);
        Field f = WeatherMappingUtil.findField(targetType, javaFieldName);
        if (f == null) return null;
        try {
            MethodHandles.Lookup lookup = MethodHandles.lookup();
            MethodHandle setterHandle = lookup.unreflectSetter(f);

            // ✅ 使用你原来的转换逻辑
            FieldConverter<Object> converter = new SpringAwareConverter(field, applicationContext);
            String jsonKey = field.getJson();
            return new FieldInstruction<>(setterHandle, converter, jsonKey);

        } catch (Exception e) {
            log.error("编译字段失败: " + javaFieldName, e);
            return null;
        }
    }


    // 假设这些方法已存在
    private <T> List<T> mapDataList(
            String json,
            WeatherProviderSchemaConfig.ProviderConfig providerCfg,
            WeatherProviderSchemaConfig.DataTypeConfig typeConfig,
            Class<T> itemClass,
            String provider) {

        List<Map<String, Object>> dataList = WeatherMappingUtil.extractDataList(json, providerCfg, typeConfig);
        if (dataList.isEmpty()) {
            log.debug("No data extracted for provider='{}', dataType='{}'", providerCfg, typeConfig);
            return Collections.emptyList();
        }

        List<T> result = dataList.stream()
                .map(item -> mapItem(item, typeConfig.getFields(), itemClass, provider))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

        // 按配置排序（如按时间戳升序）
        return SortHelper.sortByConfig(result, typeConfig.getFields());
    }


    // 核心映射方法
    private <T> T mapItem(
            Map<String, Object> source,
            List<WeatherProviderSchemaConfig.FieldMapping> fields,
            Class<T> targetType,
            String provider) {

        if (source == null || fields == null || fields.isEmpty() || targetType == null) return null;

        try {
            T instance = WeatherMappingUtil.instantiate(targetType);

            // 获取缓存的指令集并执行
            FieldInstruction<T>[] instructions = compile(targetType, fields, provider);
            for (FieldInstruction<T> inst : instructions) {
                inst.apply(instance, source, provider);
            }

            return instance;

        } catch (Exception e) {
            log.error("Failed to map item to type: " + targetType.getSimpleName() + " -> " + e.getMessage());
            return null;
        }
    }


    private <C> C parseContext(
            String json,
            String basePath,
            List<WeatherProviderSchemaConfig.FieldMapping> contextFields,
            Class<C> contextClass,
            String provider) throws Exception {

        C instance = WeatherMappingUtil.instantiate(contextClass);

        for (WeatherProviderSchemaConfig.FieldMapping field : contextFields) {
            String fullJsonPath = WeatherMappingUtil.joinJsonPath(basePath, field.getJson());
            String targetField = WeatherMappingUtil.getTargetFieldName(field);

            Object rawValue = WeatherMappingUtil.readJsonPath(json, fullJsonPath, Object.class);
            if (rawValue == null) continue;
            FieldConverter<Object> converter = new SpringAwareConverter(field, applicationContext);
            Object convertedValue = converter.convert(rawValue.toString(), provider);
//            Object convertedValue = weatherMappingFiled.convertValue(rawValue.toString(), field, provider);
            if (convertedValue != null) {
                PropertyUtils.setProperty(instance, targetField, convertedValue);
            }
        }

        return instance;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}